// 折线外角测量工具类
import * as Cesium from 'cesium'
export default class PolylineExteriorAngleMeasurer {
  /**
   * 构造函数
   * @param {Cesium.Viewer} viewer - Cesium Viewer实例
   * @param {Object} options - 配置选项
   */
  constructor(viewer, options = {}) {
    this.viewer = viewer;
    this.scene = viewer.scene;
    this.canvas = viewer.canvas;

    // 合并默认配置和用户配置
    this.options = {
      pointColor: Cesium.Color.RED,
      pointSize: 6,
      pointOutlineColor: Cesium.Color.WHITE,
      pointOutlineWidth: 2,
      lineColor: Cesium.Color.YELLOW,
      lineWidth: 2,
      tempLineColor: Cesium.Color.CYAN.withAlpha(0.7),
      tempLineWidth: 2,
      labelColor: Cesium.Color.ORANGE,
      labelFont: '18px sans-serif',
      labelOutlineColor: Cesium.Color.BLACK,
      labelOutlineWidth: 2,
      ...options
    };

    // 测量状态
    this.isMeasuring = false;
    this.points = []; // 折线点集合
    this.angles = []; // 计算的角度集合

    // 实体存储
    this.polylineEntity = null; // 折线实体
    this.pointEntities = []; // 点实体集合
    this.angleLabelEntities = []; // 角度标签实体集合
    this.tempLineEntity = null; // 临时线实体

    // 事件处理器 - 已修复拼写错误
    this.handler = new Cesium.ScreenSpaceEventHandler(this.canvas);

    // 事件回调存储
    this.events = {};

    // 绑定方法上下文
    this._onClick = this._onClick.bind(this);
    this._onMouseMove = this._onMouseMove.bind(this);
  }

  /**
   * 开始测量
   */
  start() {
    if (this.isMeasuring) return;

    this.isMeasuring = true;
    this.clear(); // 清除之前的测量结果

    // 注册鼠标点击事件
    this.handler.setInputAction(this._onClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    // 注册鼠标移动事件
    this.handler.setInputAction(this._onMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    this._triggerEvent('start', { timestamp: Date.now() });
    this.handler.setInputAction(() => {
      this.finish();
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

  /**
   * 结束测量
   */
  finish() {
    if (!this.isMeasuring) return;

    this.isMeasuring = false;

    // 移除事件监听
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    // 移除临时线
    if (this.tempLineEntity) {
      this.viewer.entities.remove(this.tempLineEntity);
      this.tempLineEntity = null;
    }

    this._triggerEvent('finish', {
      timestamp: Date.now(),
      points: this.getPoints(),
      angles: this.getAngles()
    });
  }

  /**
   * 清除所有测量结果
   */
  clear() {
    // 移除点实体
    this.pointEntities.forEach(entity => {
      this.viewer.entities.remove(entity);
    });

    // 移除折线实体
    if (this.polylineEntity) {
      this.viewer.entities.remove(this.polylineEntity);
      this.polylineEntity = null;
    }

    // 移除角度标签
    this.angleLabelEntities.forEach(entity => {
      this.viewer.entities.remove(entity);
    });

    // 移除临时线
    if (this.tempLineEntity) {
      this.viewer.entities.remove(this.tempLineEntity);
      this.tempLineEntity = null;
    }

    // 重置状态
    this.points = [];
    this.angles = [];
    this.pointEntities = [];
    this.angleLabelEntities = [];

    this._triggerEvent('clear', { timestamp: Date.now() });
  }

  /**
   * 处理鼠标点击事件
   * @param {Object} movement - 包含位置信息的对象
   * @private
   */
  _onClick(movement) {
    // 将屏幕坐标转换为世界坐标
    const cartesian = this._getCartesianFromScreen(movement.position);
    if (!cartesian) {
      console.log("无法获取有效的世界坐标");
      return;
    }

    // 添加点到集合
    this.points.push(cartesian);

    // 创建点实体（标记点）
    this._createPointEntity(cartesian);

    // 更新折线
    this._updatePolyline();

    // 当有至少3个点时，计算最新的外角
    if (this.points.length >= 3) {
      const angleInfo = this._calculateNewExteriorAngle();
      this._triggerEvent('angleCalculated', angleInfo);
    }
  }

  /**
   * 处理鼠标移动事件
   * @param {Object} movement - 包含位置信息的对象
   * @private
   */
  _onMouseMove(movement) {
    // 没有点或测量已结束，不显示临时线
    if (this.points.length === 0 || !this.isMeasuring) return;

    // 移除之前的临时线
    if (this.tempLineEntity) {
      this.viewer.entities.remove(this.tempLineEntity);
    }

    // 创建新的临时线
    const cartesian = this._getCartesianFromScreen(movement.endPosition);
    if (cartesian) {
      this.tempLineEntity = this.viewer.entities.add({
        polyline: {
          positions: [this.points[this.points.length - 1], cartesian],
          width: this.options.tempLineWidth,
          material: this.options.tempLineColor,
          clampToGround: true
        }
      });
    }
  }

  /**
   * 将屏幕坐标转换为世界坐标
   * @param {Cesium.Cartesian2} position - 屏幕坐标
   * @returns {Cesium.Cartesian3|null} 世界坐标
   * @private
   */
  _getCartesianFromScreen(position) {
    try {
      const ray = this.viewer.camera.getPickRay(position);
      if (!ray) return null;

      const cartesian = this.scene.globe.pick(ray, this.scene);
      if (!cartesian) {
        console.log("无法从该位置获取地球表面坐标");
        return null;
      }

      return cartesian;
    } catch (error) {
      console.error("坐标转换错误:", error);
      this._triggerEvent('error', {
        type: 'coordinateConversion',
        message: error.message,
        error
      });
      return null;
    }
  }

  /**
   * 创建点实体（标记点）
   * @param {Cesium.Cartesian3} position - 点位置
   * @private
   */
  _createPointEntity(position) {
    const point = this.viewer.entities.add({
      position: position,
      point: {
        color: this.options.pointColor,
        pixelSize: this.options.pointSize,
        outlineColor: this.options.pointOutlineColor,
        outlineWidth: this.options.pointOutlineWidth,
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
      },
      label: {
        text: `P${this.points.length}`, // 点标记
        font: '14px sans-serif',
        fillColor: Cesium.Color.WHITE,
        outlineColor: Cesium.Color.BLACK,
        outlineWidth: 2,
        pixelOffset: new Cesium.Cartesian2(10, 0),
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
      }
    });

    this.pointEntities.push(point);
    return point;
  }

  /**
   * 更新折线实体（连接线）
   * @private
   */
  _updatePolyline() {
    // 如果已有折线实体，先移除
    if (this.polylineEntity) {
      this.viewer.entities.remove(this.polylineEntity);
    }

    // 创建新的折线实体
    this.polylineEntity = this.viewer.entities.add({
      polyline: {
        positions: this.points,
        width: this.options.lineWidth,
        material: this.options.lineColor,
        clampToGround: true
      }
    });
  }

  /**
   * 计算最新添加的外角
   * @private
   */
  _calculateNewExteriorAngle() {
    try {
      // 取最后三个点：A, B, C，其中B是顶点
      const idx = this.points.length - 3;
      const A = this.points[idx];
      const B = this.points[idx + 1];
      const C = this.points[idx + 2];

      // 计算向量BA和BC（用于计算内角）
      const BA = Cesium.Cartesian3.subtract(A, B, new Cesium.Cartesian3());
      const BC = Cesium.Cartesian3.subtract(C, B, new Cesium.Cartesian3());

      // 计算内角（弧度）和角度
      const interiorAngleRadians = this._getAngleBetweenVectors(BA, BC);
      const interiorAngleDegrees = Cesium.Math.toDegrees(interiorAngleRadians);

      // 严格使用 360° - 内角 计算外角
      let exteriorAngleDegrees = 360 - interiorAngleDegrees;

      // 确保外角值在0°-360°范围内
      exteriorAngleDegrees = ((exteriorAngleDegrees % 360) + 360) % 360;

      // 存储角度信息
      const angleInfo = {
        vertex: B,
        value: exteriorAngleDegrees.toFixed(2),
        interiorValue: interiorAngleDegrees.toFixed(2),
        index: idx + 1, // 顶点索引
        calculation: `360° - ${interiorAngleDegrees.toFixed(2)}° = ${exteriorAngleDegrees.toFixed(2)}°`
      };
      this.angles.push(angleInfo);

      // 创建角度标签
      const labelEntity = this._createAngleLabel(angleInfo);
      angleInfo.labelEntity = labelEntity;

      return angleInfo;
    } catch (error) {
      console.error("角度计算错误:", error);
      this._triggerEvent('error', {
        type: 'angleCalculation',
        message: `计算角度时出错: ${error.message}`,
        error
      });
      return null;
    }
  }

  /**
   * 计算两个向量之间的角度（弧度）
   * @param {Cesium.Cartesian3} v1 - 向量1
   * @param {Cesium.Cartesian3} v2 - 向量2
   * @returns {number} 角度（弧度）
   * @private
   */
  _getAngleBetweenVectors(v1, v2) {
    // 计算点积
    const dotProduct = Cesium.Cartesian3.dot(v1, v2);

    // 计算向量模长
    const mag1 = Cesium.Cartesian3.magnitude(v1);
    const mag2 = Cesium.Cartesian3.magnitude(v2);

    // 防止除以零
    if (mag1 === 0 || mag2 === 0) return 0;

    // 计算余弦值
    let cosine = dotProduct / (mag1 * mag2);

    // 处理数值计算误差
    cosine = Cesium.Math.clamp(cosine, -1.0, 1.0);

    // 计算角度（弧度）
    return Math.acos(cosine);
  }

  /**
   * 创建角度标签（显示角度值）
   * @param {Object} angleInfo - 角度信息
   * @private
   */
  _createAngleLabel(angleInfo) {
    const { vertex, value, index } = angleInfo;

    // 计算标签位置
    const prevPoint = this.points[index - 1];
    const nextPoint = this.points[index + 1];

    // 计算线段长度和标签距离
    const lenBA = Cesium.Cartesian3.distance(prevPoint, vertex);
    const lenBC = Cesium.Cartesian3.distance(nextPoint, vertex);
    const labelDistance = Math.max(lenBA, lenBC) * 0.15; // 15%比例

    // 计算向量
    const v1 = Cesium.Cartesian3.subtract(prevPoint, vertex, new Cesium.Cartesian3());
    const v2 = Cesium.Cartesian3.subtract(nextPoint, vertex, new Cesium.Cartesian3());

    // 归一化向量
    Cesium.Cartesian3.normalize(v1, v1);
    Cesium.Cartesian3.normalize(v2, v2);

    // 计算角平分线方向
    const bisector = Cesium.Cartesian3.add(v1, v2, new Cesium.Cartesian3());
    Cesium.Cartesian3.normalize(bisector, bisector);

    // 计算垂直于角平分线的方向
    const up = Cesium.Cartesian3.normalize(vertex, new Cesium.Cartesian3());
    const labelDirection = Cesium.Cartesian3.cross(bisector, up, new Cesium.Cartesian3());
    Cesium.Cartesian3.normalize(labelDirection, labelDirection);

    // 计算标签位置
    const labelPosition = Cesium.Cartesian3.multiplyByScalar(
      labelDirection,
      labelDistance,
      new Cesium.Cartesian3()
    );
    Cesium.Cartesian3.add(vertex, labelPosition, labelPosition);

    // 创建标签实体（显示角度值）
    const label = this.viewer.entities.add({
      position: labelPosition,
      label: {
        text: `${value}°`, // 角度标记
        font: this.options.labelFont,
        fillColor: this.options.labelColor,
        outlineColor: this.options.labelOutlineColor,
        outlineWidth: this.options.labelOutlineWidth,
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        pixelOffset: new Cesium.Cartesian2(0, -20),
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        showBackground: true,
        backgroundColor: new Cesium.Color(0, 0, 0, 0.7),
        disableDepthTestDistance: Number.POSITIVE_INFINITY
      }
    });

    this.angleLabelEntities.push(label);
    return label;
  }

  /**
   * 触发事件
   * @param {string} eventName - 事件名称
   * @param {Object} data - 事件数据
   * @private
   */
  _triggerEvent(eventName, data) {
    if (this.events && this.events[eventName]) {
      this.events[eventName].forEach(callback => {
        try {
          callback(data);
        } catch (error) {
          console.error(`事件${eventName}的回调函数执行出错:`, error);
        }
      });
    }
  }

  /**
   * 注册事件监听
   * @param {string} eventName - 事件名称
   * @param {Function} callback - 回调函数
   */
  on(eventName, callback) {
    if (!this.events) {
      this.events = {};
    }

    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }

    this.events[eventName].push(callback);
  }

  /**
   * 移除事件监听
   * @param {string} eventName - 事件名称
   * @param {Function} callback - 要移除的回调函数
   */
  off(eventName, callback) {
    if (!this.events || !this.events[eventName]) {
      return;
    }

    const index = this.events[eventName].indexOf(callback);
    if (index !== -1) {
      this.events[eventName].splice(index, 1);
    }
  }

  /**
   * 获取所有测量的角度
   * @returns {Array} 角度信息数组
   */
  getAngles() {
    return [...this.angles];
  }

  /**
   * 获取所有测量点
   * @returns {Array} 点坐标数组
   */
  getPoints() {
    return [...this.points];
  }

  /**
   * 销毁工具
   */
  destroy() {
    this.finish();
    this.clear();
    this.handler.destroy();
    this.events = null;
  }
}
